#include "udp_server.hpp"

#define logi(_fmt, ...) \
    printf("udp[I] ");  \
    printf(_fmt "\n", ##__VA_ARGS__)
#define loge(_fmt, ...) \
    printf("udp[E] ");  \
    printf(_fmt "\n", ##__VA_ARGS__)

// 使用完记得释放指针     freeaddrinfo(addr_info);
struct addrinfo *get_udp_addrinfo(const std::string &ip, int cliceport)
{
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;    // 允许IPv4或IPv6
    hints.ai_socktype = SOCK_DGRAM; // UDP
    hints.ai_protocol = IPPROTO_UDP;

    std::string port_str = "";
    std::string ip_str = "::";
    if (cliceport > 0)
    {
        port_str = std::to_string(cliceport).c_str();
    }

    if (ip != "")
    {
        ip_str = ip;
    }

    struct addrinfo *res = nullptr;
    int ret = getaddrinfo(ip_str.c_str(), port_str.c_str(), &hints, &res);
    if (ret != 0)
    {
        loge("ip=[%s] port=[%d] getaddrinfo error! rs=%d", ip_str.c_str(), cliceport, ret);
        return nullptr; // 错误时返回空指针
    }
    // logi("ip=[%s] port=[%d] getaddrinfo ok! rs=%d", ip_str.c_str(), cliceport, ret);
    return res; // 成功时返回 addrinfo 指针
}

// int ADEALL_UDP_SERVER::create_Ipv4_fd(std::string iface, int cliceport)
// {
//     logi("bind %d:%d", iface, cliceport);
//     // 创建套接字
//     int srv_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//     if (srv_fd < 0)
//     {
//         loge("udp4 %d socket error!", cliceport);
// #ifdef _WIN32
//         closesocket(srv_fd);
// #else
//         close(srv_fd);
// #endif

//         return -1;
//     }

//     // 接收地址
//     if (cliceport > 0 || iface != "")
//     {
//         static struct sockaddr_in srv_addr;
//         memset(&srv_addr, 0, sizeof(sockaddr_in));
//         srv_addr.sin_family = AF_INET;
//         if (cliceport > 0)
//             srv_addr.sin_port = htons(cliceport); // 端口号
//         if (iface != "")
//             srv_addr.sin_addr.s_addr = inet_addr(iface.c_str()); //  绑定网卡;

//         if (bind(srv_fd, (struct sockaddr *)&srv_addr, sizeof(struct sockaddr_in)) == -1)
//         {
//             loge("udp4 %s: %d bink error!", iface.c_str(), cliceport);
// #ifdef _WIN32
//             closesocket(srv_fd);
// #else
//             close(srv_fd);
// #endif
//             return -1;
//         }
//     }
//     logi("create_Ipv4_fd %s ==> %d ok!", iface.c_str(), cliceport);
//     return srv_fd;
// }

// int ADEALL_UDP_SERVER::create_Ipv6_fd(std::string iface, int cliceport)
// {
//     // 创建套接字
//     int srv_fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
//     if (srv_fd < 0)
//     {
//         loge("udp6 %d socket error!", cliceport);
//         return -1;
//     }

//     if (cliceport > 0 || iface != "")
//     {

//         // addrinfo *ress = get_udp_addrinfo(iface, cliceport);

//         struct sockaddr_in6 srv_addr6;
//         memset(&srv_addr6, 0, sizeof(sockaddr_in6));
//         srv_addr6.sin6_family = AF_INET6;
//         srv_addr6.sin6_addr = in6addr_any;
//         if (cliceport > 0)
//             srv_addr6.sin6_port = htons(cliceport); // 端口号

//         if (iface != "") // srv_addr6.sin6_addr = ifaddr;
// #ifdef _WIN32
//             InetPton(AF_INET6, iface.c_str(), &(srv_addr6.sin6_addr));
// #else
//             inet_pton(AF_INET6, iface.c_str(), &srv_addr6.sin6_addr); // 设置目标IPv6地址
// #endif
//         if (bind(srv_fd, (struct sockaddr *)&srv_addr6, sizeof(struct sockaddr_in6)) == -1)
//         // if (bind(srv_fd, ress->ai_addr, ress->ai_addrlen) == -1)
//         {
//             loge("udp6 %d bink error!", cliceport);
// #ifdef _WIN32
//             closesocket(srv_fd);
// #else
//             close(srv_fd);
// #endif
//             // freeaddrinfo(ress);
//             return -1;
//         }
//         // freeaddrinfo(ress);
//     }
//     logi("create_Ipv6_fd %s ==> %d ok!", iface.c_str(), cliceport);

//     return srv_fd;
// }

int ADEALL_UDP_SERVER::create_Ip_fd(std::string iface, int cliceport)
{

    addrinfo *addr = get_udp_addrinfo(iface, cliceport);
    if (addr != nullptr)
    {

        // 创建套接字
        int srv_fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (srv_fd < 0)
        {
            loge("udp6 %d socket error!", cliceport);
            return -1;
        }

        if (bind(srv_fd, addr->ai_addr, addr->ai_addrlen) < 0)
        {
            loge("udp %d bink error!", cliceport);
#ifdef _WIN32
            closesocket(srv_fd);
#else
            close(srv_fd);
#endif
            freeaddrinfo(addr);
            return -1;
        }
        freeaddrinfo(addr);

        logi("create_Ip_fd %s ==> %d ok! fd=%d", iface.c_str(), cliceport, srv_fd);
        return srv_fd;
    }

    return -1;
}
sockaddr_in setSockaddr_in(const char *ip, int port)
{
    sockaddr_in send_addr;
    memset(&send_addr, 0, sizeof(sockaddr_in));
    send_addr.sin_family = AF_INET;
    send_addr.sin_port = htons(port);
#ifdef _WIN32
    send_addr.sin_addr.S_un.S_addr = inet_addr(ip);
#else
    send_addr.sin_addr.s_addr = inet_addr(ip);
#endif
    return send_addr;
}

sockaddr_in6 setSockaddr_in6(const char *ip, int port)
{
    sockaddr_in6 send_addr6;
    memset(&send_addr6, 0, sizeof(sockaddr_in6));
    send_addr6.sin6_family = AF_INET6;
    send_addr6.sin6_port = htons(port); // 端口号

#ifdef _WIN32
    InetPton(AF_INET6, ip, &(send_addr6.sin6_addr));
#else
    inet_pton(AF_INET6, ip, &send_addr6.sin6_addr); // 设置目标IPv6地址
#endif
    return send_addr6;
}
// int ADEALL_UDP_SERVER::udpSend2(const char *ip, int port, const char *data, int L, int send_fd, int ip_mode)
// {
//     if (ip_mode == 4)
//     {
//         // std::cout << "输入地址为IPv4类型" << std::endl;
//         sockaddr_in send_addr = setSockaddr_in(ip, port);
//         int rs = sendto(send_fd, data, L, 0, (struct sockaddr *)&send_addr, sizeof(sockaddr_in));
//         if (rs < 0)
//         {
//             logi("udp ip=%s port=%d send error!\n", ip, port);
//         }
//         else
//         {
//             // logi("udp ip=%s port=%d send ok ! %d\n", ip, port, srv_fd);
//             return rs;
//         }
//     }
//     else if (ip_mode == 6)
//     {
//         // result = 1; // inet_pton(AF_INET6, ip, &(sa.sin_addr));

//         struct addrinfo *res = get_udp_addrinfo(ip, port);
//         if (res == nullptr)
//             return -1;

//         // sockaddr_in6 send_addr6 = setSockaddr_in6(ip, port); //这个方法有问题
//         int rs = sendto(send_fd, data, L, 0, res->ai_addr, res->ai_addrlen);
//         freeaddrinfo(res);
//         if (rs < 0)
//         {
//             logi("udp ip6=%s port=%d send error! rs=%d", ip, port, rs);
//             // perror("sendto");
//         }
//         else
//         {
//             // logi("udp ip6=%s port=%d send ok ! %d\n", ip, port, srv_fd);
//             return rs;
//         }
//     }
//     else
//     {
//         std::cout << "输入地址无效" << std::endl;
//     }
//     return -1;
// }
int ADEALL_UDP_SERVER::udpSend2(const char *ip, int port, const char *data, int L, int send_fd)
{

    struct addrinfo *res = get_udp_addrinfo(ip, port);
    if (res == nullptr)
        return -1;

    int rs = sendto(send_fd, data, L, 0, res->ai_addr, res->ai_addrlen);
    freeaddrinfo(res);
    if (rs < 0)
    {
        logi("udp ip=%s port=%d send error! rs=%d", ip, port, rs);
        // perror("sendto");
    }
    else
    {
        // logi("udp ip6=%s port=%d send ok ! %d\n", ip, port, srv_fd);
        return rs;
    }

    return -1;
}
ADEALL_UDP_SERVER::ADEALL_UDP_SERVER(/* args */)
{
#ifdef _WIN32
    system("chcp 65001"); // 使中文不乱码
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        std::cerr << "WSAStartup failed." << std::endl;
        exit(-1);
    }
#endif
}

ADEALL_UDP_SERVER::~ADEALL_UDP_SERVER()
{
#ifdef _WIN32
    closesocket(srv_fd);
#else
    close(srv_fd);
#endif
}

void *ADEALL_UDP_SERVER::Msgthread(void *prt)
{
    ADEALL_UDP_SERVER *p = (ADEALL_UDP_SERVER *)prt;
    p->processMessages();
    return nullptr;
}
void ADEALL_UDP_SERVER::processMessages()
{
    while (1)
    {
        msg_lock.lock();
        if (!messages_queue.empty())
        {
            UDP_MSG msg = messages_queue.front();
            messages_queue.pop();
            msg_lock.unlock();
            if (_callback == nullptr)
            {
                CallBackFuntion(msg.cli_ip,
                                msg.cli_port,
                                msg.buffer,
                                _context);
            }
            else
            {
                (*_callback)(msg.cli_ip,
                             msg.cli_port,
                             msg.buffer,
                             _context);
            }
        }
        else
        {
            msg_lock.unlock();
#ifdef _WIN32
            Sleep(1);
#else
            usleep(1);
#endif
        }
    }
}

int get_addrinfo(const struct sockaddr *addr, std::string &ip, in_port_t &port)
{
    void *numeric_addr = NULL;
    char addr_buff[INET6_ADDRSTRLEN];

    if (AF_INET == addr->sa_family)
    {
        numeric_addr = &((struct sockaddr_in *)addr)->sin_addr;
        port = ntohs(((struct sockaddr_in *)addr)->sin_port);
    }
    else if (AF_INET6 == addr->sa_family)
    {
        numeric_addr = &((struct sockaddr_in6 *)addr)->sin6_addr;
        port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
    }
    else
    {
        return -1;
    }

    if (NULL != inet_ntop(addr->sa_family, numeric_addr, addr_buff, sizeof(addr_buff)))
        ip = addr_buff;
    else
        return -1;

    return 0;
}
void *ADEALL_UDP_SERVER::UDPrevthreadfunAll(void *ptr)
{
    ADEALL_UDP_SERVER *p = (ADEALL_UDP_SERVER *)ptr;
    struct sockaddr_storage cliaddr;
    socklen_t len = sizeof(cliaddr);

    while (1)
    {
        p->recvL = recvfrom(p->srv_fd, &(p->recv_buff2[0]), p->buff_size, 0, (struct sockaddr *)&cliaddr, &len);
        if (p->recvL <= 0)
        {
            continue;
        }
        else
        {
            UDP_MSG msg;
            for (size_t i = 0; i < p->recvL; i++)
            {
                msg.buffer.push_back(p->recv_buff2[i]);
            }
            msg.len = p->recvL;
            get_addrinfo((struct sockaddr *)&cliaddr, msg.cli_ip, msg.cli_port);
            p->messages_queue.push(msg);
        }
    }
    return 0;
}

int ADEALL_UDP_SERVER::Init(int cliceport, void *prt, std::string iface, int mode, size_t N, FunCallBackFuntion *fun)
{
    recv_buff2.resize(buff_size);
    ip_mode = mode;
    thread_N = N;
    _context = prt;
    _callback = fun;

    // if (ip_mode == 6)
    // {
    //     srv_fd = create_Ipv6_fd(iface, cliceport);
    //     if (srv_fd < 0)
    //     {
    //         exit(-1);
    //         return -1;
    //     }
    //     // ThreadBase(UDP6revthreadfun, this);
    // }
    // else
    // {
    //     ip_mode = 4;
    //     srv_fd = create_Ipv4_fd(iface, cliceport);
    //     if (srv_fd < 0)
    //     {
    //         exit(-1);
    //         return -1;
    //     }
    //     // ThreadBase(UDPrevthreadfun, this);
    // }
    srv_fd = create_Ip_fd(iface, cliceport);
    if (srv_fd < 0)
    {
        exit(-1);
        return -1;
    }
    ThreadBase(UDPrevthreadfunAll, this);
    for (size_t i = 0; i < thread_N; i++)
    {
        ThreadBase(Msgthread, this);
    }
    return 0;
}

void ADEALL_UDP_SERVER::MessagSend(const char *ip, int port, const char *data, int L, int fd, int mode)
{
    ScopeLocker lock(&sendcs);

    int send_fd;
    int sendmode = mode;

    if (fd > 0)
    {
        send_fd = fd;
    }
    else
    {
        send_fd = srv_fd;
        sendmode = ip_mode;
    }

    udpSend2(ip, port, data, L, send_fd);

    // udpSend(ip, port, data, L, send_fd, sendmode);
}

void ADEALL_UDP_SERVER::MessagSend(const char *ip, int port, const std::string data, int fd, int mode)
{
    MessagSend(ip, port, data.c_str(), data.length(), fd, mode);
}
void ADEALL_UDP_SERVER::CallBackFuntion(std::string ip, int port, std::vector<uint8_t> x, void *)
{
}

// 初始化组播 组播地址 组播端口 绑定网卡地址
int ADEALL_UDP_SERVER::AddMulticast(std::string Multicast_ip, std::string iface_ip)
{

    struct ip_mreq mreq;
    char group_ip[20];

    strcpy(group_ip, Multicast_ip.c_str());
    mreq.imr_multiaddr.s_addr = inet_addr(group_ip);
    mreq.imr_interface.s_addr = inet_addr(iface_ip.c_str());
    // 加入组播接收
    if (setsockopt(srv_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq)) < 0)
    {
        perror("setsockopt multicast!");
        return -1;
    }

    logi("add multicast %s ipv4 ok!%s", Multicast_ip.c_str(), iface_ip.c_str());

    return 0;
}
